﻿/* dk2Repository - Copyright (C) 2011 Daniel Kuczewski http://www.dk2.de/

 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using dk2Database.Interfaces;

namespace dk2Database.Commands
{

    /// <summary>
    /// Abstract base class for commands. Stores SQL string and parameters.
    /// </summary>
    public abstract class CommandBase : ICommand
    {
        /// <summary>
        /// Stores parameters
        /// </summary>
        public class CommandParameters : IEnumerable<CommandParameters.Parameter>
        {
            public class Parameter
            {
                public Parameter(object value)
                {
                    Value = value;
                    IsNamed = false;
                }

                public Parameter(string name, object value)
                {
                    Name = name;
                    Value = value;
                    IsNamed = true;
                }

                public bool IsNamed { get; set; }
                public string Name { get; set; }
                public object Value { get; set; }
            }


            private readonly List<Parameter> _parameters = new List<Parameter>();
            private readonly Dictionary<string, Parameter> _namedParameters = new Dictionary<string, Parameter>();

            /// <summary>
            /// Adds a named parameter with value
            /// </summary>
            public void Add(string name, object value)
            {
                if (string.IsNullOrEmpty(name))
                    _parameters.Add(new Parameter(value));
                else
                {
                    if (_namedParameters.ContainsKey(name))
                        _namedParameters[name].Value = value;
                    else
                        _namedParameters.Add(name, new Parameter(name, value));
                }
            }

            /// <summary>
            /// Adds a parameter with value
            /// </summary>
            public void Add(object value)
            {
                _parameters.Add(new Parameter(value));
            }

            public void Add(params object[] values)
            {
                foreach (var value in values)
                {
                    _parameters.Add(new Parameter(value));
                }

            }


            public IEnumerator<Parameter> GetEnumerator()
            {
                foreach (var parameter in _parameters)
                {
                    yield return parameter;
                }

                foreach (var parameter in _namedParameters)
                {
                    yield return parameter.Value;
                }
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }

        private IConnectionFactory _connectionfactory;

        public IConnectionFactory ConnectionFactory
        {
            get { return _connectionfactory ?? (_connectionfactory = Databases.Default); }
            set { _connectionfactory = value; }
        }

        public string Sql { get; set; }
        public CommandParameters Parameters{get;set;}

        
        protected CommandBase(string sql) : this(sql, new CommandParameters())
        {
            Parameters = new CommandParameters();
            Sql = sql;
        }

        protected CommandBase(string sql, params object[] parameters) : this(sql, new CommandParameters())
        {
            foreach (var parameter in parameters)
                Parameters.Add(parameter);
        }

        protected CommandBase(string sql, CommandParameters parameters)
        {
            Parameters = parameters;
            Sql = sql;

            if(Attribute.IsDefined(GetType(), typeof(DatabaseAttribute)))
            {
                var dba = (DatabaseAttribute)Attribute.GetCustomAttribute(GetType(), typeof (DatabaseAttribute));
                ConnectionFactory = Databases.Get(dba.DatabaseName);
            }

        }

        protected virtual void SetDbCommand(IDbCommand dbcmd)
        {
            dbcmd.CommandText = Sql;

            foreach (var parameter in Parameters)
            {
                if (parameter.IsNamed)
                    dbcmd.AddParameter(parameter.Name, parameter.Value);
                else
                    dbcmd.AddParameter(parameter.Value);
            }

        }

        public virtual bool OnError(CommandException ex)
        {
            return false;
        }

        public abstract void Execute();

    }

}